package cn.zhangfusheng.elasticsearch.template;

import cn.zhangfusheng.elasticsearch.constant.ElasticSearchConstant;
import cn.zhangfusheng.elasticsearch.constant.enumeration.DyanmicType;
import cn.zhangfusheng.elasticsearch.dynamic.DyanmicExecuteResult;
import cn.zhangfusheng.elasticsearch.dynamic.DynamicAnalysis;
import cn.zhangfusheng.elasticsearch.dynamic.DynamicAnalysisDetail;
import cn.zhangfusheng.elasticsearch.dynamic.sql.es.ElasticSql2DslParser;
import cn.zhangfusheng.elasticsearch.exception.GlobalSystemException;
import cn.zhangfusheng.elasticsearch.model.page.PageRequest;
import cn.zhangfusheng.elasticsearch.repository.ElasticSearchRepository;
import cn.zhangfusheng.elasticsearch.scan.ElasticSearchEntityRepositoryDetail;
import com.alibaba.fastjson2.JSON;
import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlOperation;
import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.internal.SearchContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;

/**
 * 动态字符串解析
 * @author fusheng.zhang
 * @date 2022-06-20 15:45:52
 */
public interface TemplateDynamicStrApi extends Template, ElasticSearchTemplateApi, TemplateDocumentApi {

    Logger log = LoggerFactory.getLogger(TemplateDynamicStrApi.class);

    /**
     * 执行动态字符串
     * @param entityRepositoryDetail
     * @param method
     * @param args
     * @param routing
     * @param index
     * @return
     * @throws IOException
     */
    default Object runDynamicStr(
            ElasticSearchEntityRepositoryDetail entityRepositoryDetail,
            Method method, Object[] args, String routing, String index) {
        try {
            Class<? extends ElasticSearchRepository<?>> daoClass = entityRepositoryDetail.getElasticSearchRepositoryClass();
            DynamicAnalysisDetail dynamicAnalysisDetail = new DynamicAnalysis().analysis(daoClass, method);
            DyanmicExecuteResult dyanmicExecuteResult = dynamicAnalysisDetail.execute(args);
            DyanmicType type = dyanmicExecuteResult.getType();
            switch (type) {
                case SQL: return this.runDynamicSql(entityRepositoryDetail, method, args, routing, index, dyanmicExecuteResult);
                case DSL: return this.runDynamicDsl(entityRepositoryDetail, method, args, routing, index, dyanmicExecuteResult);
                default: throw new GlobalSystemException("DyanmicType 匹配失败");
            }
        } catch (Exception e) {
            throw new GlobalSystemException(e);
        }
    }

    /**
     * 按照 es 的 query dsl 执行
     * @param entityRepositoryDetail
     * @param method
     * @param args
     * @param routing
     * @param index
     * @param dyanmicExecuteResult
     * @return
     */
    default Object runDynamicDsl(
            ElasticSearchEntityRepositoryDetail entityRepositoryDetail,
            Method method, Object[] args, String routing, String index,
            DyanmicExecuteResult dyanmicExecuteResult) throws IOException {
        String str = JSON.parseObject(dyanmicExecuteResult.getStrResult()).toJSONString();
        log.debug("dyanmic result dsl:{}", ElasticSearchConstant.PATTERN.matcher(str).replaceAll(" "));
        String[] indices = this.analysisIndex(method, args, index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(QueryBuilders.wrapperQuery(str)).trackTotalHitsUpTo(SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO);
        SearchRequest searchRequest = new SearchRequest().routing(routing).indices(indices).source(searchSourceBuilder);
        Optional<PageRequest> pageRequestOptional =
                Arrays.stream(args).filter(o -> o instanceof PageRequest).map(o -> (PageRequest) o).findFirst();
        return this.search(entityRepositoryDetail, method, searchRequest, pageRequestOptional.orElse(null));
    }

    /**
     * 按照 sql 方式执行
     * @param entityRepositoryDetail
     * @param method
     * @param args
     * @param routing
     * @param index
     * @param execute
     * @return
     * @throws IOException
     */
    default Object runDynamicSql(
            ElasticSearchEntityRepositoryDetail entityRepositoryDetail,
            Method method, Object[] args, String routing, String index,
            DyanmicExecuteResult execute) throws IOException {
        String str = execute.getStrResult();
        log.debug("dyanmic result sql:{}", ElasticSearchConstant.PATTERN.matcher(str).replaceAll(" "));
        ElasticSqlParseResult parseResult = new ElasticSql2DslParser().parse(str);
        SqlOperation sqlOperation = parseResult.getSqlOperation();
        if (sqlOperation.equals(SqlOperation.UPDATE)) {
            return this.update(parseResult.getUpdateRequest().index(index).routing(routing));
        } else if (Objects.equals(sqlOperation, SqlOperation.UPDATE_BY_QUERY)) {
            UpdateByQueryRequest updateByQueryRequest = parseResult.getUpdateByQueryRequest();
            Script script = updateByQueryRequest.getScript();
            QueryBuilder queryBuilder = updateByQueryRequest.getSearchRequest().source().query();
            String[] indices = this.analysisIndex(method, args, index);
            return this.updateByQuery(queryBuilder, script, routing, indices);
        } else if (Objects.equals(sqlOperation, SqlOperation.DELETE)) {
            return this.delete(parseResult.getDeleteRequest().index(index).routing(routing));
        } else if (Objects.equals(sqlOperation, SqlOperation.DELETE_BY_QUERY)) {
            DeleteByQueryRequest deleteByQueryRequest = parseResult.getDeleteByQueryRequest();
            QueryBuilder queryBuilder = deleteByQueryRequest.getSearchRequest().source().query();
            String[] indices = this.analysisIndex(method, args, index);
            return this.deleteByQuery(queryBuilder, routing, indices);
        } else if (Objects.equals(sqlOperation, SqlOperation.INSERT)) {
            return this.index(parseResult.getIndexRequest().routing(routing).index(index));
        } else if (Objects.equals(sqlOperation, SqlOperation.REINDEX)) {
            // TODO 动态sql reindex 待实现
        } else if (Objects.equals(sqlOperation, SqlOperation.DESC)) {
            // TODO 动态sql DESC 待实现
        }
        // 查询处理
        SearchSourceBuilder searchSourceBuilder = parseResult.getSearchRequest().source();
        searchSourceBuilder.trackTotalHitsUpTo(SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO);
        String[] indices = this.analysisIndex(method, args, index);
        SearchRequest searchRequest = parseResult.getSearchRequest()
                .source(searchSourceBuilder).routing(routing).indices(indices);
        Optional<PageRequest> pageRequestOptional = Arrays.stream(args)
                .filter(o -> o instanceof PageRequest).map(o -> (PageRequest) o).findFirst();
        return this.search(entityRepositoryDetail, method, searchRequest, pageRequestOptional.orElse(null));
    }

}
